#!/bin/bash
{
	#////////////////////////////////////
	# DietPi NordVPN
	#
	#////////////////////////////////////
	# Created by Daniel Knight / daniel.knight@dietpi.com / dietpi.com
	#
	#////////////////////////////////////
	#
	# Info:
	# - GUI to setup a NordVPN connection
	# - Allows to automatically connect on boot
	# - Allows to add post-up and pre-down scripts
	readonly USAGE='
Usage: dietpi-nordvpn [<command>]
Available commands:
  <empty>		Interactive menu to edit, connect and apply NordVPN settings
  status		Print NordVPN connection status info
'	#////////////////////////////////////

	# Grab Inputs
	INPUT=$1

	# Import DietPi-Globals --------------------------------------------------------------
	# - Only load for interactive menu
	if [[ $INPUT ]]; then

		readonly G_PROGRAM_NAME='DietPi-NordVPN'

	else

		. /boot/dietpi/func/dietpi-globals
		readonly G_PROGRAM_NAME='DietPi-NordVPN'
		G_CHECK_ROOT_USER
		G_CHECK_ROOTFS_RW
		G_INIT

	fi
	# Import DietPi-Globals --------------------------------------------------------------

	readonly FP_SETTINGS_DIETPI='/var/lib/dietpi/dietpi-software/installed/dietpi-nordvpn/settings_dietpi.conf'
	readonly FP_SETTINGS_OVPN='/var/lib/dietpi/dietpi-software/installed/dietpi-nordvpn/settings_ovpn.conf'
	readonly FP_SETTINGS_UP='/var/lib/dietpi/dietpi-software/installed/dietpi-nordvpn/up.sh'
	readonly FP_SETTINGS_DOWN='/var/lib/dietpi/dietpi-software/installed/dietpi-nordvpn/down.sh'
	readonly IFACE='tun0'
	readonly MAX_WAIT_FOR_CONNECTION=5 # seconds
	NORDVPN_USERNAME=
	NORDVPN_PASSWORD=
	NORDVPN_SERVER=
	PROTOCOL='udp'
	NORDVPN_INSTALLED=0
	NORDVPN_SERVICE=0
	NORDVPN_CONNECTED=0
	WAN_IP=
	RX='N/A'
	TX='N/A'

	Update_Wan_Ip(){ WAN_IP=$(curl -sSfLm 2 https://ifconfig.co/json 2>&1 | mawk -F\" '/"ip"/{o=$4}/"country"/{c=$4}/"region_name"/{c=$4"-"c}END{if(c){o=o" "c}if(o){print o;exit}{print}}'); }

	Init()
	{
		# Check installed
		until grep -q '^aSOFTWARE_INSTALL_STATE\[171\]=2' /boot/dietpi/.installed
		do
			# Offer install only for interactive menu
			if [[ ! $INPUT ]] && G_WHIP_YESNO "$G_PROGRAM_NAME is not installed, would you like to install it now?"
			then
				/boot/dietpi/dietpi-software install 171
			else
				TARGETMENUID=-1 # Skip menu
				return
			fi
		done
		NORDVPN_INSTALLED=1

		# Check service exists
		[[ -f '/etc/systemd/system/dietpi-nordvpn.service' ]] && NORDVPN_SERVICE=1
	}

	Check_Connected()
	{
		[[ $(ip r l dev $IFACE 2> /dev/null) ]] && NORDVPN_CONNECTED=1 || NORDVPN_CONNECTED=0
		return $(( ! $NORDVPN_CONNECTED ))
	}

	Get_Connection_Info()
	{
		RX='N/A'
		if [[ -f /sys/class/net/$IFACE/statistics/rx_bytes ]]
		then
			local rx=$(</sys/class/net/$IFACE/statistics/rx_bytes)
			[[ $rx =~ ^[0-9]+$ ]] && RX="$(( $rx / 1024**2 )) MiB"
		fi
		TX='N/A'
		if [[ -f /sys/class/net/$IFACE/statistics/tx_bytes ]]
		then
			local tx=$(</sys/class/net/$IFACE/statistics/tx_bytes)
			[[ $tx =~ ^[0-9]+$ ]] && TX="$(( $tx / 1024**2 )) MiB"
		fi
	}

	Read_Settings(){ [[ -f $FP_SETTINGS_DIETPI ]] && . $FP_SETTINGS_DIETPI; }

	Save_Settings()
	{
		if ! [[ $NORDVPN_SERVER && $NORDVPN_USERNAME && $NORDVPN_PASSWORD ]]
		then
			G_WHIP_MSG '[FAILED] You need to enter your NordVPN username + password and select a server, before settings can be applied.'
			return 1
		fi

		(( $NORDVPN_SERVICE )) && systemctl stop dietpi-nordvpn

		cat << _EOF_ > $FP_SETTINGS_OVPN
${NORDVPN_USERNAME//\'/\'\\\'\'}
${NORDVPN_PASSWORD//\'/\'\\\'\'}
_EOF_
		cat << _EOF_ > $FP_SETTINGS_DIETPI
NORDVPN_USERNAME='${NORDVPN_USERNAME//\'/\'\\\'\'}'
NORDVPN_PASSWORD='${NORDVPN_PASSWORD//\'/\'\\\'\'}'
NORDVPN_SERVER='$NORDVPN_SERVER'
PROTOCOL='$PROTOCOL'
_EOF_
		local fp_ovpn="/etc/openvpn/ovpn_$PROTOCOL/$NORDVPN_SERVER"
		G_EXEC chmod 0600 $FP_SETTINGS_OVPN $FP_SETTINGS_DIETPI $fp_ovpn
		G_EXEC chown root:root $FP_SETTINGS_OVPN $FP_SETTINGS_DIETPI $fp_ovpn

		G_CONFIG_INJECT 'auth-user-pass([[:blank:]]|$)' "auth-user-pass $FP_SETTINGS_OVPN" $fp_ovpn
		[[ -f $FP_SETTINGS_UP || -f $FP_SETTINGS_DOWN ]] && G_CONFIG_INJECT 'script-security[[:blank:]]' 'script-security 2' $fp_ovpn 'auth[[:blank:]]'
		# shellcheck disable=SC2015
		[[ -f $FP_SETTINGS_UP ]] && G_CONFIG_INJECT 'route-up[[:blank:]]' "route-up $FP_SETTINGS_UP" $fp_ovpn 'auth[[:blank:]]' || sed -i '/^[[:blank:]]*route-up[[:blank:]]/d' $fp_ovpn
		# shellcheck disable=SC2015
		[[ -f $FP_SETTINGS_DOWN ]] && G_CONFIG_INJECT 'route-pre-down[[:blank:]]' "route-pre-down $FP_SETTINGS_DOWN" $fp_ovpn 'auth[[:blank:]]' || sed -i '/^[[:blank:]]*route-pre-down[[:blank:]]/d' $fp_ovpn

		# Service: https://github.com/OpenVPN/openvpn/blob/master/distro/systemd/openvpn-client%40.service.in
		cat << _EOF_ > /etc/systemd/system/dietpi-nordvpn.service
[Unit]
Description=NordVPN (DietPi)
Wants=network-online.target
After=network-online.target dietpi-boot.service

[Service]
Type=notify
ExecStart=$(command -v openvpn) --suppress-timestamps --nobind --config $fp_ovpn
KillMode=process
# Hardenings
PrivateTmp=true
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE
LimitNPROC=10
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
ProtectSystem=true
ProtectHome=true

[Install]
WantedBy=multi-user.target
_EOF_
		systemctl daemon-reload
		NORDVPN_SERVICE=1
		G_EXEC systemctl restart dietpi-nordvpn

		local i=1
		until Check_Connected || (( $i > $MAX_WAIT_FOR_CONNECTION ))
		do
			G_DIETPI-NOTIFY -2 "Waiting for connection ($((i++))/$MAX_WAIT_FOR_CONNECTION)"
			sleep 1
		done

		if (( $NORDVPN_CONNECTED ))
		then
			G_DIETPI-NOTIFY 0 "Connection established: $NORDVPN_SERVER"
		else
			G_DIETPI-NOTIFY 1 "Connection failed/timeout: $NORDVPN_SERVER"
			G_WHIP_MSG "Connection failed/timeout: $NORDVPN_SERVER\n\nPlease verify account details are correct.\n\nIf problems persist, please check the service status: \"systemctl -l status dietpi-nordvpn\""
		fi
	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Whip
	#/////////////////////////////////////////////////////////////////////////////////////
	TARGETMENUID=0
	LAST_SELECTED_NAME='Username'

	Menu_Exit()
	{
		G_WHIP_SIZE_X_MAX=50
		G_WHIP_YESNO "Exit $G_PROGRAM_NAME?" && TARGETMENUID=-1 # Exit
	}

	# TARGETMENUID=0
	Menu_Main()
	{
		local text_status="WAN IP       : $WAN_IP\nState        : "
		if (( ! $NORDVPN_SERVICE ))
		then
			text_status+='Not configured\nSubscription : https://go.nordvpn.net/aff_c?offer_id=15&aff_id=5305&url_id=902'

		elif ! Check_Connected
		then
			text_status+='Disconnected\nSubscription : https://go.nordvpn.net/aff_c?offer_id=15&aff_id=5305&url_id=902'
		else
			Get_Connection_Info
			text_status+="Connected\nTraffic      : Sent = $TX | Received = $RX"
		fi

		G_WHIP_MENU_ARRAY=(

			'' '●─ Global Options '
			'Username' ": [$NORDVPN_USERNAME]"
			'Password' ": [${NORDVPN_PASSWORD//?/*}]"
			'Server' ": [$NORDVPN_SERVER]"
			'Update' ': This updates the NordVPN server list'
		)
		if (( $NORDVPN_SERVICE ))
		then
			local autostart_enabled=0 autostart_text='Off'
			systemctl -q is-enabled dietpi-nordvpn && { autostart_enabled=1 autostart_text='On'; }
			G_WHIP_MENU_ARRAY+=(

				'Autostart' ": [$autostart_text]"
				'Refresh' ': Update VPN connection status'
			)
			(( $NORDVPN_CONNECTED )) && G_WHIP_MENU_ARRAY+=('Disconnect' '')
		fi
		G_WHIP_MENU_ARRAY+=(

			'' '●─ Connection Up and Down Scripts '
			'Edit Up' ': This script gets executed right after the VPN is connected'
			'Edit Down' ': This script gets executed right before the VPN is disconnected'
			'' '●─ Save Settings '
			'Apply' ': Save settings and restart VPN connection'
		)

		G_WHIP_DEFAULT_ITEM=$LAST_SELECTED_NAME
		G_WHIP_BUTTON_CANCEL_TEXT='Exit'
		if G_WHIP_MENU "$text_status"
		then
			LAST_SELECTED_NAME=$G_WHIP_RETURNED_VALUE

			if [[ $G_WHIP_RETURNED_VALUE == 'Apply' ]]
			then
				Save_Settings
				Update_Wan_Ip

			elif [[ $G_WHIP_RETURNED_VALUE == 'Edit'* ]]
			then
				local fp=$FP_SETTINGS_UP
				[[ $G_WHIP_RETURNED_VALUE == *'Down' ]] && fp=$FP_SETTINGS_DOWN
				[[ -f $fp ]] || echo -e '#!/bin/bash\n# Clear this file completely, including line breaks, to have it removed.' > $fp
				nano $fp
				if [[ -s $fp ]]
				then
					G_EXEC_NOEXIT=1 G_EXEC chmod 0700 $fp
				else
					G_EXEC_NOEXIT=1 G_EXEC rm $fp
				fi

			elif [[ $G_WHIP_RETURNED_VALUE == 'Autostart' ]]
			then
				if (( $autostart_enabled ))
				then
					G_EXEC_NOEXIT=1 G_EXEC systemctl disable dietpi-nordvpn
				else
					G_EXEC_NOEXIT=1 G_EXEC systemctl enable dietpi-nordvpn
				fi

			elif [[ $G_WHIP_RETURNED_VALUE == 'Username' ]]
			then
				G_WHIP_DEFAULT_ITEM=$NORDVPN_USERNAME
				G_WHIP_INPUTBOX 'Please enter your NordVPN username:' && NORDVPN_USERNAME=$G_WHIP_RETURNED_VALUE

			elif [[ $G_WHIP_RETURNED_VALUE == 'Password' ]]
			then
				G_WHIP_PASSWORD 'Please enter your NordVPN password:' && NORDVPN_PASSWORD=$result
				unset -v result

			elif [[ $G_WHIP_RETURNED_VALUE == 'Disconnect' ]]
			then
				G_EXEC systemctl stop dietpi-nordvpn
				Update_Wan_Ip
				LAST_SELECTED_NAME='Refresh'

			elif [[ $G_WHIP_RETURNED_VALUE == 'Server' ]]
			then
				# Select protocol
				G_WHIP_MENU_ARRAY=(

					'UDP' ': Recommended'
					'TCP' ''
				)

				G_WHIP_DEFAULT_ITEM=$PROTOCOL
				G_WHIP_MENU 'Please select the connection protocol type:' && PROTOCOL=${G_WHIP_RETURNED_VALUE,,}

				# Select server
				G_DIETPI-NOTIFY 2 'Populating NordVPN server list, please wait...'
				G_WHIP_MENU_ARRAY=()
				for i in /etc/openvpn/"ovpn_$PROTOCOL"/*.ovpn
				do
					G_WHIP_MENU_ARRAY+=("${i##*/}" '')
				done

				G_WHIP_DEFAULT_ITEM=$NORDVPN_SERVER
				G_WHIP_MENU 'Please select a NordVPN server to use:' && NORDVPN_SERVER=$G_WHIP_RETURNED_VALUE

			elif [[ $G_WHIP_RETURNED_VALUE == 'Refresh' ]]
			then
				Update_Wan_Ip

			elif [[ $G_WHIP_RETURNED_VALUE == 'Update' ]]
			then
				G_WHIP_YESNO 'This will remove all NordVPN server files and perform a fresh download of NordVPN server configurations. Once completed, you will need to reselect a NordVPN server from the new list.\n\nDo you want to continue?' || return 0
				G_EXEC curl -sSfLO 'https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip'
				NORDVPN_SERVER=
				G_EXEC_DESC='Removing old NordVPN server configs' G_EXEC rm -Rf /etc/openvpn/ovpn_{tcp,udp}/*.nordvpn.com.{tcp,udp}.ovpn
				G_EXEC unzip -o ovpn.zip -d/etc/openvpn
				G_EXEC_NOEXIT=1 G_EXEC rm ovpn.zip
			fi
		else
			Menu_Exit
		fi
	}

	#/////////////////////////////////////////////////////////////////////////////////////
	# Main Loop
	#/////////////////////////////////////////////////////////////////////////////////////
	#-----------------------------------------------------------------------------------
	Init
	#-----------------------------------------------------------------------------------
	if [[ $INPUT == 'status' ]]
	then
		if (( ! $NORDVPN_INSTALLED ))
		then
			echo -e "\e[33m$G_PROGRAM_NAME is not installed! Please run: \"dietpi-software install 171\"\e[0m"

		elif (( ! $NORDVPN_SERVICE ))
		then
			echo -e "\e[33m$G_PROGRAM_NAME is not configured! Please run: \"dietpi-nordvpn\"\e[0m"

		elif ! Check_Connected
		then
			echo -e '\e[1;31mDisconnected\e[0m'
		else
			Get_Connection_Info
			echo -e "\e[1;32mConnected\e[0m - Sent = $TX | Received = $RX"
		fi

	elif [[ $INPUT ]]
	then
		# Unknown input
		echo -e "\e[90m[\e[0m\e[31mFAILURE\e[0m\e[90m]\e[0m \e[90m$G_PROGRAM_NAME | \e[0mInvalid input command ($INPUT). Aborting...\n$USAGE"
		exit 1
	else
		Read_Settings
		Update_Wan_Ip

		until (( $TARGETMENUID < 0 ))
		do
			Menu_Main
		done
	fi
	#-----------------------------------------------------------------------------------
	exit
	#-----------------------------------------------------------------------------------
}
